package n.info.xml;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

import n.info.entities.Feed;
import n.info.entities.FeedChannel;
import n.info.entities.FeedItem;
import n.info.entities.FeedMedia;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import android.sax.Element;
import android.sax.ElementListener;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.util.Xml;


public class SaxFeedParser {

	// ~ constants .................................................................................
	
	//	XML tags
	static final String RSS = "rss" ;
	static final String CHANNEL = "channel" ;
	static final String PUB_DATE = "pubDate";
	static final  String DESCRIPTION = "description";
	static final  String LINK = "link";
	static final  String TITLE = "title";
	static final  String ITEM = "item";
	static final String ENCLOSURE = "enclosure" ;
	static final String URL = "url" ;
	static final String LENGTH = "length" ;
	static final String TYPE = "type" ;
	
	 static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
	
	// Atom tags
	static final String FEED = "feed" ;
	static final String ENTRY = "entry" ;
	static final String SUMMARY = "summary";
	
	// ~ Static fields/initializers .............................................................
	// ~ Static methods ........................................................................
	// ~ Instance fields .........................................................................
	
	final URL feedUrl;
	
	// ~ Constructors ...........................................................................
	
    public SaxFeedParser(String feedUrl) throws MalformedURLException {
    	 try {
    	        this.feedUrl = new URL(feedUrl);
    	 } 
    	 catch (MalformedURLException e) {
    	        throw e ;
    	 }
    }

    // ~ Properties ...............................................................................
 	// ~ Abstract methods ...................................................................
 	// ~ Abstract methods implementations .......................................
 	// ~ Interface implementations ......................................................
 	// ~ Methods ..................................................................................	
    
    private InputStream getInputStream() {
        try {
            return feedUrl.openConnection().getInputStream();
        } 
        catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public Feed parse() throws IOException, SAXException {    
        
        final Feed feed = new Feed() ;
        final FeedChannel feedChannel = new FeedChannel() ;
        final FeedItem feedItem = new FeedItem() ;
        final FeedMedia feedMedia = new FeedMedia() ;
        
        RootElement root = new RootElement(RSS);
        
        // CHANNEL PARSING
        Element channel = root.getChild(CHANNEL);
        channel.getChild(TITLE).setEndTextElementListener(new EndTextElementListener() {
			public void end(String body) {
				feedChannel.setTitle(body) ;
			}
		});
        channel.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener() {
			public void end(String body) {
				feedChannel.setDescription(body) ;
			}
		});
        channel.getChild(LINK).setEndTextElementListener(new EndTextElementListener() {
			public void end(String body) {
				feedChannel.setLink(body);
			}
		});
        channel.setEndElementListener(new EndElementListener() {
			public void end() {
				feed.setChannel(feedChannel);
			}
		});
        
        // ITEM PARSING
        Element item = channel.getChild(ITEM);
        item.setEndElementListener(new EndElementListener(){
            public void end() {
            	feed.addItem(feedItem.copy());
            	feedItem.erase();
            }
        });
        item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
                feedItem.setTitle(body);
            }
        });
        item.getChild(LINK).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
            	 feedItem.setLink(body);
            }
        });
        item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
            	 feedItem.setDescription(body.replaceAll("\\<.*?>",""));
            }
        });
       
        // ENCLOSURE PARSING
        Element enclosure = item.getChild(ENCLOSURE);
        enclosure.setElementListener(new ElementListener() {
			public void start(Attributes attributes) {
				feedMedia.setUrl(attributes.getValue(URL)) ;
				feedMedia.setLength(attributes.getValue(LENGTH)) ;
				feedMedia.setType(attributes.getValue(TYPE)) ;
			}
			public void end() {
				feedItem.setEnclosure(feedMedia.copy());
				feedMedia.erase();
			}
		});
        
        // DO PARSE
        Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
        
        return feed;
    }
    
    public Feed parseAtom() throws IOException, SAXException {    
        
        final Feed feed = new Feed() ;
        final FeedItem feedItem = new FeedItem() ;
               
        RootElement root = new RootElement(ATOM_NAMESPACE, FEED);
                
        // ENTRY PARSING
        Element item = root.getChild(ATOM_NAMESPACE, ENTRY);
        item.setEndElementListener(new EndElementListener(){
            public void end() {
            	feed.addItem(feedItem.copy());
            	feedItem.erase();
            }
        });
        item.getChild(ATOM_NAMESPACE, TITLE).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
                feedItem.setTitle(body);
            }
        });
        // need handling link
        // example :
        //<link length="0" href="http://q.liberation.fr/photo/335818/?modified_at=1318077330&amp;ratio_x=03&amp;ratio_y=02&amp;width=150" type="image/jpeg" rel="enclosure"></link>
        item.getChild(ATOM_NAMESPACE, LINK).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
            	 feedItem.setLink(body);
            }
        });
        item.getChild(ATOM_NAMESPACE, SUMMARY).setEndTextElementListener(new EndTextElementListener(){
            public void end(String body) {
            	 feedItem.setDescription(body);
            }
        });
               
        // DO PARSE
        Xml.parse(new InputStreamReader(this.getInputStream()), root.getContentHandler());
        
        return feed;
    }
    
    
    // ~ Inner classes ...........................................................................
}